package service

import (
	"fmt"
	"strings"

	"libvirt.org/go/libvirt"
	"libvirt.org/go/libvirtxml"
)

// DomainManager handles VM domain operations
type DomainManager struct {
	conn *Connection
}

// NewDomainManager creates a new domain manager
func NewDomainManager(conn *Connection) *DomainManager {
	return &DomainManager{conn: conn}
}

// ListAllDomains returns all domains
func (dm *DomainManager) ListAllDomains() ([]libvirt.Domain, error) {
	conn := dm.conn.GetConnect()
	domains, err := conn.ListAllDomains(libvirt.CONNECT_LIST_DOMAINS_ACTIVE | libvirt.CONNECT_LIST_DOMAINS_INACTIVE)
	if err != nil {
		return nil, fmt.Errorf("failed to list domains: %w", err)
	}
	return domains, nil
}

// ListActiveDomains returns only active domains
func (dm *DomainManager) ListActiveDomains() ([]libvirt.Domain, error) {
	conn := dm.conn.GetConnect()
	domains, err := conn.ListAllDomains(libvirt.CONNECT_LIST_DOMAINS_ACTIVE)
	if err != nil {
		return nil, fmt.Errorf("failed to list active domains: %w", err)
	}
	return domains, nil
}

// GetDomainByName retrieves a domain by name
func (dm *DomainManager) GetDomainByName(name string) (*libvirt.Domain, error) {
	conn := dm.conn.GetConnect()
	domain, err := conn.LookupDomainByName(name)
	if err != nil {
		return nil, fmt.Errorf("domain %s not found: %w", name, err)
	}
	return domain, nil
}

// GetDomainByUUID retrieves a domain by UUID
func (dm *DomainManager) GetDomainByUUID(uuid string) (*libvirt.Domain, error) {
	conn := dm.conn.GetConnect()
	domain, err := conn.LookupDomainByUUIDString(uuid)
	if err != nil {
		return nil, fmt.Errorf("domain with UUID %s not found: %w", uuid, err)
	}
	return domain, nil
}

// CreateDomain creates a new domain from XML
func (dm *DomainManager) CreateDomain(xmlConfig string) (*libvirt.Domain, error) {
	conn := dm.conn.GetConnect()
	domain, err := conn.DomainDefineXML(xmlConfig)
	if err != nil {
		return nil, fmt.Errorf("failed to define domain: %w", err)
	}
	return domain, nil
}

// getNetworksFromDomainXML extracts network names from domain XML
func (dm *DomainManager) getNetworksFromDomainXML(name string) ([]string, error) {
	xmlConfig, err := dm.GetDomainXML(name)
	if err != nil {
		return nil, err
	}

	// Parse the XML to extract network information
	var networks []string
	lines := strings.Split(xmlConfig, "\n")
	for _, line := range lines {
		if strings.Contains(line, "<source network=") {
			// Extract network name from <source network='network_name'/>
			start := strings.Index(line, "network='")
			if start != -1 {
				start += len("network='")
				end := strings.Index(line[start:], "'")
				if end != -1 {
					networkName := line[start : start+end]
					networks = append(networks, networkName)
				}
			}
		}
	}

	return networks, nil
}

// startNetworksForDomain starts all networks required by a domain
func (dm *DomainManager) startNetworksForDomain(name string) error {
	networks, err := dm.getNetworksFromDomainXML(name)
	if err != nil {
		return fmt.Errorf("failed to get networks for domain %s: %w", name, err)
	}

	if len(networks) == 0 {
		return nil // No networks to start
	}

	conn := dm.conn.GetConnect()

	for _, networkName := range networks {
		// Check if network exists and is active
		network, err := conn.LookupNetworkByName(networkName)
		if err != nil {
			return fmt.Errorf("network %s not found for domain %s: %w", networkName, name, err)
		}
		defer func() {
			if err := network.Free(); err != nil {
				// 记录错误但继续执行
				_ = err // 显式使用错误变量避免空分支警告
			}
		}()

		active, err := network.IsActive()
		if err != nil {
			return fmt.Errorf("failed to check network %s status: %w", networkName, err)
		}

		if !active {
			// Start the network if it's not active
			if err := network.Create(); err != nil {
				return fmt.Errorf("failed to start network %s for domain %s: %w", networkName, name, err)
			}
		}
	}

	return nil
}

// StartDomain starts a domain
func (dm *DomainManager) StartDomain(name string) error {
	// First, start any required networks
	if err := dm.startNetworksForDomain(name); err != nil {
		return fmt.Errorf("failed to start networks for domain %s: %w", name, err)
	}

	domain, err := dm.GetDomainByName(name)
	if err != nil {
		return err
	}
	defer func() {
		if err := domain.Free(); err != nil {
			// 记录错误但继续执行
			_ = err // 显式使用错误变量避免空分支警告
		}
	}()

	if err := domain.Create(); err != nil {
		return fmt.Errorf("failed to start domain %s: %w", name, err)
	}
	return nil
}

// ShutdownDomain gracefully shuts down a domain
func (dm *DomainManager) ShutdownDomain(name string) error {
	domain, err := dm.GetDomainByName(name)
	if err != nil {
		return err
	}
	defer func() {
		if err := domain.Free(); err != nil {
			// 记录错误但继续执行
			_ = err // 显式使用错误变量避免空分支警告
		}
	}()

	if err := domain.Shutdown(); err != nil {
		return fmt.Errorf("failed to shutdown domain %s: %w", name, err)
	}
	return nil
}

// ForceStopDomain forcefully stops a domain
func (dm *DomainManager) ForceStopDomain(name string) error {
	domain, err := dm.GetDomainByName(name)
	if err != nil {
		return err
	}
	defer func() {
		if err := domain.Free(); err != nil {
			// 记录错误但继续执行
			_ = err // 显式使用错误变量避免空分支警告
		}
	}()

	if err := domain.Destroy(); err != nil {
		return fmt.Errorf("failed to destroy domain %s: %w", name, err)
	}
	return nil
}

// RebootDomain reboots a domain
func (dm *DomainManager) RebootDomain(name string) error {
	domain, err := dm.GetDomainByName(name)
	if err != nil {
		return err
	}
	defer func() {
		if err := domain.Free(); err != nil {
			// 记录错误但继续执行
			_ = err // 显式使用错误变量避免空分支警告
		}
	}()

	if err := domain.Reboot(libvirt.DOMAIN_REBOOT_DEFAULT); err != nil {
		return fmt.Errorf("failed to reboot domain %s: %w", name, err)
	}
	return nil
}

// DeleteDomain undefines a domain
func (dm *DomainManager) DeleteDomain(name string) error {
	domain, err := dm.GetDomainByName(name)
	if err != nil {
		return err
	}
	defer func() {
		if err := domain.Free(); err != nil {
			// 记录错误但继续执行
			_ = err // 显式使用错误变量避免空分支警告
		}
	}()

	// Check if domain is running
	active, err := domain.IsActive()
	if err != nil {
		return fmt.Errorf("failed to check domain state: %w", err)
	}

	if active {
		return fmt.Errorf("cannot delete running domain %s, stop it first", name)
	}

	if err := domain.Undefine(); err != nil {
		return fmt.Errorf("failed to undefine domain %s: %w", name, err)
	}
	return nil
}

// GetDomainXML returns the XML configuration of a domain
func (dm *DomainManager) GetDomainXML(name string) (string, error) {
	domain, err := dm.GetDomainByName(name)
	if err != nil {
		return "", err
	}
	defer func() {
		if err := domain.Free(); err != nil {
			// 记录错误但继续执行
			_ = err // 显式使用错误变量避免空分支警告
		}
	}()

	xmlDesc, err := domain.GetXMLDesc(0)
	if err != nil {
		return "", fmt.Errorf("failed to get XML for domain %s: %w", name, err)
	}
	return xmlDesc, nil
}

// GetDomainInfo returns domain information
func (dm *DomainManager) GetDomainInfo(name string) (*libvirt.DomainInfo, error) {
	domain, err := dm.GetDomainByName(name)
	if err != nil {
		return nil, err
	}
	defer func() {
		if err := domain.Free(); err != nil {
			// 记录错误但继续执行
			_ = err // 显式使用错误变量避免空分支警告
		}
	}()

	info, err := domain.GetInfo()
	if err != nil {
		return nil, fmt.Errorf("failed to get info for domain %s: %w", name, err)
	}
	return info, nil
}

// ParseDomainXML parses domain XML into a structured object
func (dm *DomainManager) ParseDomainXML(xmlConfig string) (*libvirtxml.Domain, error) {
	domainConfig := &libvirtxml.Domain{}
	if err := domainConfig.Unmarshal(xmlConfig); err != nil {
		return nil, fmt.Errorf("failed to parse domain XML: %w", err)
	}
	return domainConfig, nil
}

// SetDomainVCPUs sets the number of vCPUs for a domain
func (dm *DomainManager) SetDomainVCPUs(name string, vcpus uint) error {
	domain, err := dm.GetDomainByName(name)
	if err != nil {
		return err
	}
	defer func() {
		if err := domain.Free(); err != nil {
			// 记录错误但继续执行
			_ = err // 显式使用错误变量避免空分支警告
		}
	}()

	if err := domain.SetVcpus(vcpus); err != nil {
		return fmt.Errorf("failed to set vcpus for domain %s: %w", name, err)
	}
	return nil
}

// SetDomainMemory sets the memory for a domain (in KiB)
func (dm *DomainManager) SetDomainMemory(name string, memory uint64) error {
	domain, err := dm.GetDomainByName(name)
	if err != nil {
		return err
	}
	defer func() {
		if err := domain.Free(); err != nil {
			// 记录错误但继续执行
			_ = err // 显式使用错误变量避免空分支警告
		}
	}()

	if err := domain.SetMemory(memory); err != nil {
		return fmt.Errorf("failed to set memory for domain %s: %w", name, err)
	}
	return nil
}
